package com.xes.hardwritingrecognition.opencv;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Path;
import android.support.annotation.NonNull;
import android.util.Log;

import com.xes.hardwritingrecognition.ImageProcessor;

import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import static org.opencv.imgproc.Imgproc.COLOR_BGR2GRAY;

/**
 * Simple job when process a {@link Path}
 *
 * @author wl
 * @since 2018/11/30 下午6:59
 */
public class OpenCVPathProcessor implements ImageProcessor<Mat> {
    private static final String TAG = "OpenCVPathProcessor";

    static {
        System.loadLibrary("opencv_java");
    }

    private Context context;
    private boolean isDebugMode;
    private String processTimeStr;

    public OpenCVPathProcessor(Context context) {
        this.context = context;
    }

    @Override
    public void setDebugMode(boolean debugMode) {
        isDebugMode = debugMode;
    }

    @Override
    public Mat process(@NonNull Bitmap bitmap) {
        processTimeStr = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        Mat tmpMat = new Mat(bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8UC3);
        Utils.bitmapToMat(bitmap, tmpMat);

        printMat(tmpMat, "original");
        return processImage(tmpMat);
    }

    /**
     * Convert a Mat object into Bitmap, which can be displayed when debugging. If <code>saveFileTag</code>
     * is provided, bitmap will be saved as: {sdcard}/{app-file-dir}/hwrImage/{processTime}/{saveFileTag}.jpg
     *
     * @param mat src Mat in type CV_8Uxx
     * @param saveFileTag if provided, save bitmap
     */
    private void printMat(Mat mat, String saveFileTag) {
        if (isDebugMode) {
            Bitmap bitmap = Bitmap.createBitmap(mat.width(), mat.height(), Bitmap.Config.ARGB_8888);
            Utils.matToBitmap(mat, bitmap);
            Log.d(TAG, "printMat: bitmap" + bitmap);
            if (saveFileTag != null) {
                saveBitmap(bitmap, saveFileTag);
            }
        }
    }

    private void saveBitmap(Bitmap bitmap, String tag) {
        try {
            File dir = new File(context.getExternalFilesDir("hwrImage"), processTimeStr);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            File filePic = new File(dir, tag + ".png");
            if (!filePic.exists()) {
                filePic.createNewFile();
            }
            FileOutputStream fos = new FileOutputStream(filePic);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
            fos.flush();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private Mat processImage(Mat mat) {
        // 1. 获取灰度图
        Mat grayImage;
        if (mat.channels() == 1) {
            grayImage = mat;
        } else {
            grayImage = new Mat();
            Imgproc.cvtColor(mat, grayImage, COLOR_BGR2GRAY);//灰度
        }
        mat.release();
        printMat(grayImage, null);
        // 2. 二值化
        Mat binaryImage = new Mat();
        Imgproc.threshold(grayImage, binaryImage, 0, 255, Imgproc.THRESH_OTSU);//二值化
        grayImage.release();
        printMat(binaryImage, "th1");
        // 3. 膨胀
//        Mat dilateImage = new Mat();
////        double dilateRate = calcuDilateRate(binaryImage);
//        double dilateRate = 2;
//        Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(dilateRate, dilateRate));
//        Imgproc.dilate(binaryImage, dilateImage, element);//膨胀
//        printMat(dilateImage, null);
        // 4. 缩放
        Mat scaleImage = imageScale(binaryImage);
        printMat(scaleImage, "scale");
        // 5. 二值化
        Mat binary2Image = new Mat();
        Imgproc.threshold(scaleImage, binary2Image, 0, 255, Imgproc.THRESH_OTSU);//二值化
        scaleImage.release();
        printMat(binary2Image, "th2");
        // 6. 转float类型
        Mat floatImage = new Mat(28, 28, CvType.CV_32FC1);
        binary2Image.convertTo(floatImage, CvType.CV_32FC1);
        binary2Image.release();
        // 7. 1维化
        Mat destImage = floatImage.reshape(0, 1).clone();
        floatImage.release();
//        Core.normalize(destImage, destImage, 0.0, 1.0, Core.NORM_MINMAX); //归一
        return destImage;
    }

    private double calcuDilateRate(Mat srcImage) {
        int t_cols = srcImage.cols();
        int t_rows = srcImage.rows();
        double rate;
        if (t_rows >= t_cols) {
            double h = 20d;
            rate = t_rows / h;
        } else {
            double w = 20d;
            rate = t_cols / w;
        }
        return rate;
    }

    //srcImage需要调整大小的图像
    //功能：对图像调整成tensorflow的输入大小：该大小为：较长的边调整到20，较短的边等比例调整，上下左右用黑色像素填充，最后图像成为28*28的大小
    private Mat imageScale(Mat srcImage) {
        int t_cols = srcImage.cols();
        int t_rows = srcImage.rows();
        Mat resizeImage = new Mat();
        Mat borderImage = new Mat();
        if (t_rows >= t_cols) {
            double h = 20d;
            double w = Math.max(Math.round(h * t_cols / t_rows), 1d);
            Imgproc.resize(srcImage, resizeImage, new Size(w, h));
            int left = (28 - resizeImage.cols()) / 2;
            Imgproc.copyMakeBorder(resizeImage, borderImage, 4, 4, left, 28 - resizeImage.cols() - left, Imgproc.BORDER_CONSTANT, new Scalar(0, 0, 0));
        } else {
            double w = 20d;
            double h = Math.max(Math.round(w * t_rows / t_cols), 1d);
            Imgproc.resize(srcImage, resizeImage, new Size(w, h));
            int top = (28 - resizeImage.rows()) / 2;
            Imgproc.copyMakeBorder(resizeImage, borderImage, top, 28 - resizeImage.rows() - top, 4, 4, Imgproc.BORDER_CONSTANT, new Scalar(0, 0, 0));
        }
        srcImage.release();
        resizeImage.release();
        return borderImage;
    }
}